<?php

declare(strict_types=1);

namespace Vipkwd\SDK\OAuth;

use \Vipkwd\SDK\OAuth\Restful;
use \Vipkwd\SDK\OAuth\OAuth as SDK;
use \Vipkwd\SDK\OAuth\Storage\Session;
use \Vipkwd\SDK\OAuth\Dependents\Utils;

class Action
{
    private static $JwtHS256Salt = 'admin000';
    public function __set($key, $value)
    {
        // 通过__set 阻止外部修改当前实例
    }

    /**
     * 获取用户信息
     * 
     * @param integer|null|bool
     * 		--int 获取指定用户信息
     * 		--true 远程获取当前用户信息
     * 		--null 获取本地缓存用户信息
     * @return array|null
     */
    static function getUserInfo($uid = null)
    {
        if (!in_array(gettype($uid), ['integer', 'string'])) {
            if ($uid === null) {
                return self::cacheOAuthUser();
            }
            $uid = self::cacheOAuthToken('user_id', null, false);
        }

        $user = Restful::get('/v1/user/' . $uid, [], static::cacheOAuthToken('access_token', null, false));
        $user = $user['data'] ?? [];

        //刷新本地已缓存的当前登录用户信息
        if (isset($user['id']) && $user['id'] == self::cacheOAuthToken('user_id', null, false)) {
            self::cacheOAuthUser($user);
        }
        return $user;
    }

    /**
     * 
     * @return integer|string
     */
    static function getUserId()
    {
        $token = self::cacheOAuthToken(null, null, false);
        if (!empty($token)) {
            @date_default_timezone_set('PRC');
            if ($token['user_id'] && $token['expires_time'] > time()) {
                return $token['user_id'];
            }
        }
        return 0;
    }

    static function getClientId(string $clientId = null)
    {
        return is_string($clientId) ? $clientId : (SDK::$sdk->clientId ?? 'client_id_is_invalid');
    }

    /**
     * 
     * @return string|null
     */
    static function getAccessToken()
    {
        return self::cacheOAuthToken('access_token', null, false);
    }
    /**
     * 获取/设置 Oauth用户信息
     * 
     * @return array|bool|null
     */
    static function cacheOAuthUser($value = null, $clientId = null)
    {
        $clientId = self::getClientId($clientId);

        if (is_array($value)) {
            return Session::set('oauth_user', $value, $clientId);
        }
        if (Session::has('oauth_user', $clientId)) {
            $userInfo = Session::get('oauth_user', [], $clientId);
            if (isset($userInfo['id']) && $userInfo['id'] > 0) {
                return (is_string($value) && $value) ? ($userInfo[$value] ?? null) : $userInfo;
            }
            // return self::getUserInfo();
        }
        if ($value === null)
            return [];
        return null;
    }
    /**
     * 清除Oauth用户信息
     * 
     * @return bool
     */
    static function deleteOAuthUser($clientId = null)
    {
        return Session::set('oauth_user', null, self::getClientId($clientId));
    }

    /**
     * 获取/设置 OauthToken信息
     * @param mixed $value
     * @param string|null $clientId
     * @param boolean $autoRefreshToken <true>
     * 
     * @return array|null|string|integer|bool
     */
    static function cacheOAuthToken($value = null, $clientId = null, bool $autoRefreshToken = true)
    {
        $clientId = self::getClientId($clientId);
        if (is_array($value)) {
            Utils::log($value, 'oAuthToken: set');
            return Session::set('oauth_token', $value, $clientId);
        }

        if (Session::has('oauth_token', $clientId)) {
            $tokenData = Session::get('oauth_token', '', $clientId);
            if (is_array($tokenData)) {
                @date_default_timezone_set('PRC');
                $expiresTimeBefore = $tokenData['expires_time'] - 3;

                if ($autoRefreshToken === true && isset($tokenData['refresh_token']) && $expiresTimeBefore < time()) {
                    $tokenData = SDK::$sdk->refreshToken($tokenData['refresh_token'], true, function ($tokenData, $userInfo) use ($clientId) {
                        Utils::log($tokenData, 'oAuthToken: set(refresh callback)');
                        static::cacheOAuthToken($tokenData, $clientId);
                        static::cacheOAuthUser($userInfo, $clientId);
                    });
                    if (!is_array($tokenData)) {
                        $tokenData = [];
                        Utils::log($tokenData, 'oAuthToken: set(refresh response)');
                    }
                    return (is_string($value) && $value) ? ($tokenData[$value] ?? null) : $tokenData;
                }
                if ($tokenData['expires_time'] > time()) {
                    Utils::log($tokenData, 'oAuthToken: get');
                    return (is_string($value) && $value) ? ($tokenData[$value] ?? null) : $tokenData;
                }
                self::deleteOAuthToken();
                self::deleteOAuthUser();
            }
            Utils::log([], 'oAuthToken: get(local empty:exists tokenData)');
            return [];
        }
        if ($value === null) {
            Utils::log([], 'oAuthToken: get(local empty:un-exists tokenData)');
            return [];
        }
        Utils::log(null, "oAuthToken: get:field: {$value}");
        return null;
    }
    /**
     * 清除Oauth用户信息
     * 
     * @return bool
     */
    static function deleteOAuthToken($clientId = null)
    {
        return Session::set('oauth_token', null, self::getClientId($clientId));
    }

    static function encodeToJwtAccessToken(array $tokenData = null, string $clientId = null)
    {
        $clientId = self::getClientId($clientId);
        if ($tokenData === null && $clientId) {
            $tokenData = static::cacheOAuthToken(null, $clientId);
        }
        if ($clientId && is_array($tokenData) && isset($tokenData['access_token']) && isset($tokenData['user_id'])) {
            if ($tokenData['access_token'] && $tokenData['user_id'] && ($tokenData['expires_time'] > time())) {
                $data = json_encode([
                    // "user_id" => $tokenData['user_id'],
                    // "access_token" => $tokenData['access_token'],
                    "expires_in" => $tokenData['expires_in'],
                    "refresh_token" => $tokenData['refresh_token'] ?? '',
                    "refresh_token_time" => $tokenData['refresh_token_time'] ?? '',
                ]);

                $header = Utils::base64Encode(["typ" => 'JWT', "alg" => 'HS256']);

                $playload = Utils::base64Encode([
                    'id' => $tokenData['access_token'], // for BC (see #591)
                    'jti' => $tokenData['access_token'],
                    'aud' => $clientId,
                    'sub' => $tokenData['user_id'],
                    'exp' => $tokenData['expires_time'],
                    'scope' => $tokenData['scope'],
                    'token_type' => $tokenData['token_type'],
                    'iss' => 'vipkwd.oauth',
                    'iat' => $tokenData['expires_time'] - $tokenData['expires_in'],
                    'hash' => Utils::cryptRC4($data, 'encode', $clientId)
                ]);
                $signature = Utils::base64Encode(Utils::encryptSha256($header . '.' . $playload, self::$JwtHS256Salt));

                return implode('.', [
                    $header,
                    $playload,
                    $signature
                ]);
            }
        }
        return '';
    }

    static function decodeJwtAccessToken(string $jwt, string $type = 'accessToken')
    {
        if (!strpos($jwt, '.')) {
            return false;
        }

        $tks = explode('.', $jwt);

        if (count($tks) != 3) {
            return false;
        }

        list($headb64, $payloadb64, $cryptob64) = $tks;

        if (null === ($header = json_decode(Utils::urlSafeB64Decode($headb64), true))) {
            return false;
        }

        if (null === $payload = json_decode(Utils::urlSafeB64Decode($payloadb64), true)) {
            return false;
        }

        if (!isset($header['alg'])) {
            return false;
        }

        $signature = Utils::base64Encode(Utils::encryptSha256($headb64 . '.' . $payloadb64, self::$JwtHS256Salt));

        if (!Utils::hash_equals($signature, $cryptob64)) {
            return false;
        }
        if (!isset($payload['hash']) && $type == 'accessToken') {
            return false;
        }

        $hash = $payload['hash'] ?? '';
        $payload['hash'] = '';

        if ($hash) {
            if ($data = Utils::cryptRC4($hash, 'D', $payload['aud'])) {
                $data = json_decode($data, true);
                if (is_array($data) && !empty($data)) {
                    $data['access_token'] = $payload['jti'];
                    $data['expires_time'] = $payload['exp'];
                    $data['scope'] = $payload['scope'];
                    $data['token_type'] = $payload['token_type'];
                    $data['user_id'] = $payload['sub'];
                    $data['client_id'] = $payload['aud'];
                    ksort($data);
                }
            }
            $payload['hash'] = $data ?: '';
        }
        return ($type == 'accessToken') ? ($payload['hash'] && isset($payload['hash']['access_token']) ? $payload['hash'] : []) : $payload;
    }
}